﻿using System;
using System.Collections.Generic;
using System.Linq;
using Best.SmartCms.Core.Utility;
using Best.SmartCms.Dto.Common;
using Best.SmartCms.Dto.Response;
using Best.SmartCms.Service.Security;
using Best.SmartCms.Utility;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;

namespace Best.SmartCms.API.WebAPIExtensions
{
    /// <summary>
    /// 
    /// </summary>
    public class ActionSignatureAttribute : TypeFilterAttribute
    {
        /// <summary>
        /// 
        /// </summary>
        public ActionSignatureAttribute() : base(typeof(ActionSignatureImpl))
        {
            Arguments = new object[] { };
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class SignatureAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 启用状态
        /// </summary>
        public bool Enabled;

        /// <summary>
        /// 超时时间，默认30分钟，单位秒
        /// </summary>
        public long TimeOutSecond;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="timeOut">超时时间（秒）</param>
        public SignatureAttribute(long timeOut = 60 * 30)
        {
            TimeOutSecond = timeOut;
        }
    }


    /// <summary>
    /// 签名验证
    /// </summary>
    public class ActionSignatureImpl : ActionFilterAttribute
    {
        private readonly BusinessSystemService _businessSystemService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="businessSystemService">验证参数</param>
        public ActionSignatureImpl(BusinessSystemService businessSystemService)
        {
            _businessSystemService = businessSystemService;
        }



        /// <summary>
        /// Action 执行前验证
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnActionExecuting(ActionExecutingContext actionContext)
        {
            if (!(actionContext.Filters.FirstOrDefault(f => f is SignatureAttribute) is SignatureAttribute signFilter))
            {
                return;
            }

            var timeOutSecond = signFilter.TimeOutSecond;
            var _params = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); ;
            var actionParameters = actionContext.ActionDescriptor.Parameters;
            // 1、变量简单类型
            //foreach (var p in actionParameters)
            //{
            //    if (IsSimpleType(p.ParameterType))
            //    {
            //        if (sigParams.Contains(p.Name))
            //        {
            //            var value = actionContext.ActionArguments[p.Name];
            //            _params.Add(p.Name, value?.ToString() ?? "");
            //        }
            //    }
            //}

            // 2、如果签名还是没有全部赋值，查询复杂类型,这里只判断OA命名空间下面自定义类型

            foreach (var p in actionParameters)
            {
                if (p.ParameterType.FullName.StartsWith("Best."))
                {
                    //var fobject = actionContext.ActionArguments[p.Name];
                    actionContext.ActionArguments.TryGetValue(p.Name, out var fobject);

                    if (fobject != null)
                    {
                        Type t = fobject.GetType();

                        System.Reflection.PropertyInfo[] properties = t.GetProperties();
                        foreach (System.Reflection.PropertyInfo info in properties)
                        {
                            if (info.CustomAttributes.Any(c => c.AttributeType == typeof(SignatureValidationAttribute)))
                            {
                                object value = info.GetValue(fobject, null);
                                if (value != null)
                                {
                                    _params.Add(info.Name, IsSimpleType(value.GetType())
                                        ? (value.ToString() ?? "")
                                        : (JsonConvert.SerializeObject(value) ?? ""));
                                }
                                else
                                {
                                    _params.Add(info.Name, "");
                                }
                            }
                        }
                    }
                }
            }


            // 3、如果还是有没有赋值，说明没有设置相关参数，异常输出
            if (!_params.ContainsKey("timestamp") || !_params.ContainsKey("accesskey") || !_params.ContainsKey("token"))
            {
                actionContext.Result = new JsonResult(new ApiResponse
                {
                    Code = ResponseCode.TokenError,
                    Message = "签名参数：timestamp、accesskey、token赋值",
                    Success = false
                });

                return;
            }

            var timestamp = _params["timestamp"];
            var accessKey = _params["accesskey"];
            var secret = _businessSystemService.GetSecretByAccessKey(accessKey);
            if (string.IsNullOrEmpty(secret))
            {
                actionContext.Result = new JsonResult(new ApiResponse
                {
                    Code = ResponseCode.TokenError,
                    Message = $"未找到accesskey={accessKey}对应的私钥",
                    Success = false
                });
                return;
            }

            var token = _params["token"];
            _params.Remove("token");
            try
            {
                NLogManager.LogTrace($"验签参数{JsonConvert.SerializeObject(_params)}：accessKey={accessKey},timestamp={timestamp},token={token},TimeOutSecond={timeOutSecond}");
                SignatureService.CheckSignature(_params, accessKey, secret, timestamp, token, timeOutSecond);
            }
            catch (Exception ex)
            {
                actionContext.Result = new JsonResult(new ApiResponse
                {
                    Code = ResponseCode.TokenError,
                    Message = $"签名发生错误：{ex.Message}",
                    Success = false
                });
            }
        }

        /// <summary>
        /// 判断简单类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static bool IsSimpleType(Type type)
        {
            if (type == typeof(string) || type == typeof(int) || type == typeof(long) || type == typeof(bool) ||
                type == typeof(short) ||
                type == typeof(double) || type == typeof(DateTime) || type == typeof(float))
            {
                return true;
            }

            return false;
        }
    }
}